package com.cn.delan.core.mht;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;

import com.cn.delan.core.encrypt.Base64;
import com.cn.delan.core.util.StringUtil;

public class MHTWriter {
	private static final String imgRX = "<[img|IMG][^>]+src=([^\\s>]+)[^>]*>";
	private static final String cssRX = "<link[^>]+href=([^\\s>]+)[^>]*>";
	private static final String jsRX = "<script[^>]+src=([^\\s>]+)[^>]*></script>";
	//暂时不支持框架结构
	private static final String frameRX = "<i?frame[^>]+src=([^\\s>]+)[^>]*>";
	private static final String imgRXCSS = "url\\(([^\\)]+)\\)";
	
	private String from = "德兰软件";
	private String subject = "德兰软件自动生成的 MIME格式多方邮件";
	private String body = "这是由德兰软件自动生成的 MIME格式多方邮件。";
	private ArrayList<String> pathList = new ArrayList<String>();
	private String path;
	private String pageContent;
	private String boundary;
	private String date;
	private String charset = "GBK";
	private StringBuffer parameters = new StringBuffer();
	private URL url;
	/**
	 * 初始化MHTWriter对象,采用默认编码GBK
	 * @param path 文档路径.支持http://和file:/协议
	 * @throws MalformedURLException
	 */
	public MHTWriter(String path) throws MalformedURLException{
		this.path = path;
		this.url = new URL(this.path);
		this.boundary = UUID.randomUUID().toString().replaceAll("-", "_");
	}
	/**
	 * 初始化MHTWriter对象
	 * @param path 文档路径.支持http://和file:/协议
	 * @param charset 文档编码集,用于参数编码和文档内容获取等
	 * @throws MalformedURLException
	 */
	public MHTWriter(String path,String charset) throws MalformedURLException{
		this(path);
		this.charset = charset;
	}
	/**
	 * 为访问增加参数信息
	 * @param paramName 参数名
	 * @param paramString 参数值
	 * @throws UnsupportedEncodingException
	 */
	public void addParameter(String paramName,String paramString) throws UnsupportedEncodingException{
		if(this.parameters.length()!=1)
			this.parameters = this.parameters.append("&");
		this.parameters = this.parameters.append(paramName);
		this.parameters = this.parameters.append("="+URLEncoder.encode(paramString,this.charset));
	}
	/**
	 * 获取文档内容
	 * @return 文档内容
	 * @throws IOException
	 */
	public String getPageContent() throws IOException{
		if (this.pageContent == null) {
			URLConnection connection = this.url.openConnection();
			if (this.parameters.length() != 0) {
				connection.setDoOutput(true);
				OutputStream outputStream = connection.getOutputStream();
				outputStream.write(this.parameters.toString().getBytes());
				outputStream.flush();
				outputStream.close();
			}
			if (connection.getContentType()!=null && connection.getContentType().toLowerCase().startsWith("text/html")) {
				InputStream inputStream = connection.getInputStream();
				StringBuffer buffer = new StringBuffer();
				InputStreamReader isr = new InputStreamReader(inputStream);
				BufferedReader br = new BufferedReader(isr);
				String line = null;
				while ((line = br.readLine()) != null)
					buffer = buffer.append(line+"\r\n");
				this.pageContent = buffer.toString();
				inputStream.close();
			} else {
				if (connection.getContentType() != null)
					throw new IOException("初始化地址非text/html页面文件,而是[" + connection.getContentType() + "]格式!");
				else
					throw new IOException("网络连接出错或地址参数有问题!\r\n" + this.path);
			}
		}
		return this.pageContent;
	}
	/**
	 * 解析所有资源信息
	 * @throws IOException
	 */
	private void find() throws IOException{
		//先处理样式表,因为样式表中可能包含图片信息
		this.find(MHTWriter.cssRX);
		//先处理脚本,因为脚本中可能包含图片信息
		this.find(MHTWriter.jsRX);
		this.find(MHTWriter.imgRX);
		//对框架进行支持
		this.find(MHTWriter.frameRX);
	}
	/**
	 * 根据正则表达式解析对应资源
	 * @param ex 正则表达式
	 * @throws IOException
	 */
	private void find(String ex) throws IOException{
		List<String[]> list = StringUtil.matcher(this.getPageContent(), ex);
		for(String []strs:list){
			String attachPath = this.getAbstractPath(this.url,strs[1]);
			if(!this.pathList.contains(attachPath)){
				this.pathList.add(attachPath);
			}
		}
	}
	/**
	 * 资源相对路径转换为绝对路径
	 * @param url 当前资源url地址
	 * @param path 相对路径地址
	 * @return 返回完整的绝对路径
	 */
	private String getAbstractPath(URL url,String path){
		String apath = path;
		if(apath.startsWith("\"")){
			apath = apath.substring(1);
		}
		if(apath.endsWith("\"")){
			apath = apath.substring(0,apath.length()-1);
		}
		if(apath.startsWith("\'")){
			apath = apath.substring(1);
		}
		if(apath.endsWith("\'")){
			apath = apath.substring(0,apath.length()-1);
		}
		URL absoluteURL = null;
		try {
			absoluteURL = new URL(url, apath);
			apath = absoluteURL.toString();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		return apath;
	}
	/**
	 * 保存页面文档,文件名称为将扩展名替换为mht,最后一个.后的字符串替换为mht
	 * @return 保存的完整路径
	 * @throws IOException
	 */
	public String save() throws IOException{
		String fileName = this.url.getFile();
		fileName = fileName.substring(0,fileName.lastIndexOf("."));
		return this.save(fileName.toLowerCase().endsWith(".mht")?fileName:fileName+".mht");
	}
	/**
	 * 保存页面文档到指定路径
	 * @param MHTPath 保存到的路径
	 * @return 保存到的文件完整路径
	 * @throws IOException
	 */
	public String save(String MHTPath) throws IOException{
		return this.save(new File(MHTPath));
	}
	/**
	 * 保存页面文档到文件对象
	 * @param MHTFile 保存到的文件对象
	 * @return 保存到的文件完整路径
	 * @throws IOException
	 */
	public String save(File MHTFile) throws IOException{
		if(MHTFile==null){
			return null;
		}
		this.find();
		File parent = MHTFile.getParentFile();
		if(parent!=null && !parent.exists()){
			parent.mkdirs();
		}
		FileWriter writer = new FileWriter(MHTFile);
		this.save(writer);
		return MHTFile.getAbsolutePath();
	}
	/**
	 * 保存页面文档到输出流
	 * @param writer 输出流
	 * @throws IOException
	 */
	public void save(Writer writer) throws IOException{
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(System.currentTimeMillis());
		this.date = calendar.getTime().toString();
		//保存头部信息
		writer.write("From: "+this.from+"\r\n");
		writer.write("Subject: "+this.subject+"\r\n");
		writer.write("Date: "+this.date+"\r\n");
		writer.write("MIME-Version: 1.0\r\n");
		writer.write("Content-Type: multipart/related;\r\n");
		writer.write("\ttype=\"text/html\";\r\n");
		writer.write("\tboundary=\"----=_NextPart_"+this.boundary+"\"\r\n");
		writer.write("X-MimeOLE: Produced By Microsoft MimeOLE V6.0.6001.18049\r\n");
		writer.write("\r\n");
		writer.write(this.body+"\r\n");
		writer.write("\r\n");
		//保存页面HTML信息
		writer.write("------=_NextPart_"+this.boundary+"\r\n");
		writer.write("Content-Type: text/html;\r\n");
	 	writer.write("\tcharset=\""+charset+"\"\r\n");
	 	writer.write("Content-Transfer-Encoding: 7bit\r\n");
	 	writer.write("Content-Location: "+this.path+"\r\n");
	 	writer.write("\r\n");
	 	writer.write(this.pageContent);
	 	writer.write("\r\n");
	 	writer.write("\r\n");
	 	//保存相关资源信息
	 	for(int i=0;i<this.pathList.size();i++){
	 		this.saveAttach(writer, this.pathList.get(i));
	 	}
	 	//保存结束信息
		writer.write("------=_NextPart_"+this.boundary+"--");
		writer.write("\r\n");
		writer.close();
	}
	/**
	 * 保存附件信息到输出流
	 * @param writer 输出流
	 * @param path 附件路径
	 * @throws IOException
	 */
	private void saveAttach(Writer writer,String path) throws IOException{
		String contentType = "text/html";
		String encoding = "7bit";
		if(path.toLowerCase().endsWith("js")){
			contentType = "application/x-js";
			encoding = "7bit";
		}else
		if(path.toLowerCase().endsWith("css")){
			contentType = "application/x-css";
			encoding = "7bit";
		}else
		if(path.toLowerCase().endsWith("jpg") || path.toLowerCase().endsWith("jpeg")){
			contentType = "image/jpeg";
			encoding = "base64";
		}else
		if(path.toLowerCase().endsWith("gif")){
			contentType = "image/gif";
			encoding = "base64";
		}else
		if(path.toLowerCase().endsWith("png")){
			contentType = "image/png";
			encoding = "base64";
		}else
		if(path.toLowerCase().endsWith("mht")){
			//不支持mht
			return;
		}
		else{
			//框架中的资源,扩展名不能确定如:htm,html,jsp,do,action,php,asp,aspx等
			contentType = "text/html";
			encoding = "7bit";
			MHTWriter mht = new MHTWriter(path);
			//查找相关资源,但不进行保存
			mht.find();
			//框架中的资源保存到资源列表
			this.pathList.addAll(mht.getPathList());
		}
		//头部信息
		writer.write("------=_NextPart_"+this.boundary+"\r\n");
		writer.write("Content-Type: "+contentType+"\r\n");
		writer.write("Content-Transfer-Encoding: "+encoding+"\r\n");
		writer.write("Content-Location: "+path+"\r\n");
		writer.write("\r\n");
		
		InputStream is = null;
		byte []b = null;
		int len = -1;
		//分隔符,普通文本没有分隔符,base64加密后的资源以\r\n为分隔符
		String split = "";
		URL attachUrl = new URL(path);
		//普通文本保存前资源初始化
		if(encoding.equals("7bit")){
			is = attachUrl.openStream();
			b = new byte[50000];
			split = "";
		}
		//base64资源保存前资源初始化
		if(encoding.equals("base64")){
			is = Base64.encode(attachUrl.openStream());
			b = new byte[76];
			split = "\r\n";
		}
		while((len=is.read(b))!=-1){
			String s = new String(b,0,len);
			//CSS样式表再解析
			if(path.toLowerCase().endsWith("css")){
				List<String[]> list = StringUtil.matcher(s, MHTWriter.imgRXCSS);
				if(list!=null){
					for(String []arrays:list){
						//URL地址重定位
						String imgpath = this.getAbstractPath(attachUrl,arrays[1]);
						if(!this.pathList.contains(imgpath)){
							this.pathList.add(imgpath);
						}
					}
				}
			}
			writer.write(s+split);
		}
		//普通文本保存完后需要补充一个\r\n分隔符
		if(encoding.equals("7bit")){
			writer.write("\r\n");
		}
		writer.write("\r\n");
	}
	
	public String getFrom() {
		return from;
	}
	public void setFrom(String from) {
		this.from = from;
	}
	public String getSubject() {
		return subject;
	}
	public void setSubject(String subject) {
		this.subject = subject;
	}
	public String getBody() {
		return body;
	}
	public void setBody(String body) {
		this.body = body;
	}
	public ArrayList<String> getPathList() {
		return pathList;
	}
	public void print(){
		for(String s:this.pathList){
			System.out.println(s);
		}
		System.out.println(this.pathList.size());
	}
	public static void main(String []args) throws Exception{
		MHTWriter writer = new MHTWriter("file:/E:/我的资源/HTML页面/新华舆情/test.htm");
		//System.out.println(writer.getPageContent());
		System.out.println(writer.save());
		writer.print();
	}
}
